Skip to content

Conversation

@coodos
Copy link
Contributor

@coodos coodos commented Nov 14, 2025

Description of change

Cerberus (or rather group-charter-manager-api) had an issue that it only sent updates after you edited an already edited charter and there were no messages via cerberus before that.

Issue Number

Type of change

  • Fix (a change which fixes an issue)

How the change has been tested

Change checklist

  • I have ensured that the CI Checks pass locally
  • I have removed any unnecessary logic
  • My code is well documented
  • I have signed my commits
  • My code follows the pattern of the application
  • I have self reviewed my code

Summary by CodeRabbit

  • Performance

    • Webhook processing for charter changes is now non-blocking, improving responsiveness.
  • Bug Fixes

    • Groups without a charter are skipped from syncing.
    • Group locking/unlocking during provisioning fixed to prevent sync conflicts.
  • Improvements

    • Simplified charter update handling (clears signatures when charter changes).
    • Added a delayed/manual sync after charter + eName provisioning to ensure timely synchronization.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 14, 2025

Walkthrough

Converted webhook charter processing to non-blocking async calls; added creation-time locking and evault-related unlocking with a delayed (5s) manual sync; simplified charter-change signature deletion; and added a subscriber guard to skip syncing groups that lack a charter.

Changes

Cohort / File(s) Change Summary
Webhook async handling
platforms/cerberus/src/controllers/WebhookController.ts
Converted two processCharterChange calls from awaited (blocking) calls to non-blocking asynchronous calls and attached .catch handlers to log errors, preserving webhook responsiveness.
Group locking and delayed manual sync
platforms/group-charter-manager-api/src/controllers/GroupController.ts
Added adapter import; lock group on creation via adapter.addToLockedIds(group.id) and unlock when provisioning via eVault; after provisioning with charter+ename, wait 5 seconds, re-fetch full group, and call adapter.handleChange for a manual sync with error handling.
Charter-change signature deletion
platforms/group-charter-manager-api/src/services/GroupService.ts
Simplified logic to detect charter changes by direct property comparison (currentGroup.charter !== groupData.charter) and delete signatures unconditionally when changed before saving in a single merge-and-save flow.
Charter presence guard in subscriber
platforms/group-charter-manager-api/src/web3adapter/watchers/subscriber.ts
Added guard in handleChange to skip processing groups table entries when data.charter is absent, preventing syncs of groups without a charter.

Sequence Diagram(s)

sequenceDiagram
    participant Webhook as Webhook (Cerberus)
    participant GroupCtrl as GroupController (API)
    participant GroupSvc as GroupService (API)
    participant Adapter as Adapter
    participant Subscriber as Subscriber

    alt New Group Creation
        Webhook->>+GroupCtrl: POST /group (create)
        GroupCtrl->>Adapter: addToLockedIds(group.id)
        GroupCtrl->>+GroupSvc: save new group
        GroupSvc-->>-GroupCtrl: saved group
        GroupCtrl-->>-Webhook: 201 Created
        Note right of Webhook: processCharterChange called async\n(with .catch) — no await
    end

    alt Charter Update / Provision via eVault
        Webhook->>+GroupCtrl: PATCH /groups/:id (update charter)
        GroupCtrl->>+GroupSvc: updateGroup (merge & save)
        GroupSvc->>GroupSvc: detect charterChanged\ndelete signatures if changed
        GroupSvc-->>-GroupCtrl: updated group
        GroupCtrl->>Adapter: removeFromLockedIds(group.id) (if needsEVault)
        GroupCtrl->>GroupCtrl: wait 5s
        GroupCtrl->>+GroupSvc: re-fetch full group
        GroupCtrl->>Adapter: handleChange("groups", fullGroup) (manual sync)
        GroupCtrl-->>-Webhook: 200 OK
        Note right of Webhook: processCharterChange called async\n(with .catch) — no await
    end

    alt Downstream subscriber processing
        Webhook->>Subscriber: processCharterChange (async)
        Subscriber->>Subscriber: validate data.id
        Note over Subscriber: if table == "groups" AND no charter -> skip
        Subscriber->>Subscriber: continue processing if charter present
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to GroupController.ts: locking/unlocking interactions with adapter state and the 5-second delay + re-fetch/manual sync path.
  • Verify WebhookController.ts: non-blocking calls with .catch do not swallow actionable errors and logging is sufficient.
  • Inspect GroupService.ts: ensure charter comparison covers edge cases (null/undefined vs empty) and signature deletion is safe.
  • Check subscriber.ts: confirm the charter-presence guard doesn't block legitimate updates (e.g., planned empty-charter workflows).

Possibly related PRs

  • #339 — Related changes touching GroupService and delayed downstream sync behavior for charter/ename handling.
  • #349 — Related adjustments in WebhookController processing and async/error handling for charter change processing.
  • #426 — Similar modifications to when and how processCharterChange is invoked and its async/error-handling.

Suggested reviewers

  • sosweetham
  • ananyayaya129

Poem

🐰 I hopped in code to nip the block,
Fired off tasks and beat the clock,
Locks go on, then off with grace,
Five seconds pause — then sync takes place,
A rabbit cheers the faster race! 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: addressing an issue where Cerberus only triggered updates after subsequent edits to an already edited charter.
Description check ✅ Passed The description covers the main issue and includes the type of change, but lacks details on how the fix was tested and doesn't reference a specific issue number.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/cerberus-triggers-only-after-edit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
platforms/group-charter-manager-api/src/controllers/GroupController.ts (1)

123-147: Unlock + delayed manual sync achieves the desired behavior but tightly couples to adapter internals

Using needsEVault = !group.charter && !group.ename && charter to gate eVault provisioning and then unlocking the group (via direct lockedIds.splice) before calling updateGroup ensures that the first “real” charter+ename update can sync out. The follow‑up setTimeout that re-fetches the full group and calls adapter.handleChange gives you a belt‑and‑suspenders sync so Cerberus sees complete data, but it may duplicate what the subscriber already does on afterUpdate and relies on direct mutation of adapter.lockedIds.

This is fine as a targeted workaround, especially with the explicit // HACK comment, though longer‑term you might want an explicit unlock helper on the adapter and a dedicated “manual resync group(id)” path to avoid scattering adapter internals and timers across controllers.

Also applies to: 149-155, 164-183

🧹 Nitpick comments (2)
platforms/group-charter-manager-api/src/web3adapter/watchers/subscriber.ts (1)

200-207: Guard for charterless groups looks correct; clarify semantics if empty charter should ever sync

The early return for tableName === "groups" && !data.charter will effectively prevent any group without a truthy charter from syncing, which matches the new “only sync once charter exists” flow. If you ever need to distinguish between undefined/null and an intentionally-empty string charter, you may want to tighten this condition (e.g., check for data.charter?.trim()), but as-is it seems consistent with how other code treats charters.

platforms/cerberus/src/controllers/WebhookController.ts (1)

176-187: Making processCharterChange fire‑and‑forget aligns with non‑blocking webhook handling

Dropping await and using .catch means charter-change processing no longer impacts the webhook’s HTTP status or latency, which directly addresses the “only triggers after edit” symptom by decoupling Cerberus from the main path. Just be aware this also means any failures here will be visible only via logs, not via webhook responses or retries; if reliability becomes a concern, you might later want a lightweight queue or metric around these async calls.

Also applies to: 224-233

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 490ae34 and 719cde8.

📒 Files selected for processing (4)
  • platforms/cerberus/src/controllers/WebhookController.ts (2 hunks)
  • platforms/group-charter-manager-api/src/controllers/GroupController.ts (3 hunks)
  • platforms/group-charter-manager-api/src/services/GroupService.ts (1 hunks)
  • platforms/group-charter-manager-api/src/web3adapter/watchers/subscriber.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
platforms/group-charter-manager-api/src/controllers/GroupController.ts (1)
platforms/group-charter-manager-api/src/web3adapter/watchers/subscriber.ts (1)
  • adapter (15-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
platforms/group-charter-manager-api/src/services/GroupService.ts (1)

37-48: Charter-change detection and pre-save signature deletion are well-scoped

The charterChanged check is precise (only fires when a new charter value is actually provided and differs) and deleting all signatures before the save ensures ORM subscribers and downstream processing never see stale signatures tied to the old charter. This nicely simplifies the flow to a single save while preserving correct invariants around signatures.

platforms/group-charter-manager-api/src/controllers/GroupController.ts (1)

4-6: Group creation lock is a reasonable way to prevent premature syncs; verify adapter export

Locking the group ID right after creation ensures subscriber‑driven syncs won’t push a charterless group out to the adapter, which fits the new provisioning flow. Just double‑check that adapter is re‑exported from "../web3adapter" (and not only from the watcher module) so this import doesn’t create a brittle or circular dependency.

Also applies to: 31-36

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
platforms/group-charter-manager-api/src/controllers/GroupController.ts (2)

149-155: Avoid mutating adapter.lockedIds directly; prefer an encapsulated unlock helper

Here you’re reaching into adapter.lockedIds and manually splicing by index, while locking uses adapter.addToLockedIds(group.id). That asymmetry leaks adapter internals and risks breakage if lockedIds changes shape or if IDs can be added more than once.

Consider introducing a matching API (e.g. adapter.removeFromLockedIds(id) or an internal helper) and using that here, or at least centralizing the filter/splice logic in one place rather than in the controller. Also worth double‑checking that id from params is exactly the same type/format as the IDs stored via group.id to avoid unlock misses.


164-182: Manual delayed sync works but could use hardening and de‑coupling

The 5s setTimeout with a re‑fetch and adapter.handleChange is a pragmatic fix, but a few things are worth tightening:

  • This may duplicate work with the normal watcher → handleChange path; ensure adapter.handleChange is idempotent for group updates so double syncs are harmless.
  • JSON.parse(JSON.stringify(completeGroup)) assumes the entity is fully JSON‑safe (no Dates/BigInts/custom types); if the ORM provides a serializer (toJSON/toPlain), it would be safer to use that instead.
  • The 5s delay is a magic number; consider making it configurable if this hack sticks around.

Functionally this block looks fine and won’t block the HTTP response, but you might want to revisit these points before declaring the behavior “final”.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 719cde8 and 6aa6389.

📒 Files selected for processing (1)
  • platforms/group-charter-manager-api/src/controllers/GroupController.ts (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
platforms/group-charter-manager-api/src/controllers/GroupController.ts (1)
platforms/group-charter-manager-api/src/web3adapter/watchers/subscriber.ts (1)
  • adapter (15-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
platforms/group-charter-manager-api/src/controllers/GroupController.ts (2)

5-5: Adapter import into controller looks appropriate

Importing the shared adapter here keeps locking/unlocking and manual sync logic using the same singleton instance as the watcher setup, which is what you want for consistent Cerberus behavior.


31-33: Locking new groups on creation aligns with intended sync gating

Locking the group immediately after creation so it doesn’t sync until it has charter+ename fits the Cerberus fix (no premature “empty” group updates going out). The placement after createGroup but before participant mutations is reasonable.

@coodos coodos merged commit 9221926 into main Nov 14, 2025
4 checks passed
@coodos coodos deleted the fix/cerberus-triggers-only-after-edit branch November 14, 2025 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants